package app_kvClient;

import infrastructure.ExceptionHandler;
import infrastructure.LoggingManager;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import common.messages.KVMessage;

import client.Commands;
import client.KVCommInterface;
import client.KVStore;
import client.KVStorageListener;
import clientCommunication.CommunicationAsync;
import clientCommunication.ICommunication;



public class KVClient implements Runnable, KVStorageListener {
	private Logger logger = LoggingManager.CreateClientLogger(KVClient.class);
	private KVCommInterface storage;	
	private boolean isRunning;
	private String CONSOLE_MESSAGE = "EchoClient>";
	
	/** Start up of the console application. 
	 * @param args
	 */
	public static void main(String[] args) {		
		new KVClient().run();
	}
	
	public KVClient () {		
	}
		
	@Override
	public void run() {
		logger.info("Starting Application");
		Println("Welcome to EchoClient DanieLondon. Version 3.0.");		
		this.isRunning = true;
		// Console Application. Read Option of the User
		BufferedReader input = new BufferedReader(new InputStreamReader(System.in));		
		logger.debug("Input Stream Reader created");
		Print(CONSOLE_MESSAGE);
		while(this.isRunning)
		{	
			try{
				String inputOption = input.readLine();
				logger.info(inputOption);
				handleInputUser(inputOption);				
			}catch(Exception ex){
				logger.error(ex);
				ExceptionHandler.Handle(ex);
				Print(CONSOLE_MESSAGE);
			}			
		}	
		
		// Tear down the communication, in case that the client has not diconnected it.
		try{
			storage.disconnect();
		}catch(Exception ex){
			logger.warn("Storage disconnect has failed.");
		}
		
		Println("Bye");
		logger.info("Application closed.");
	}
	
	@Override
	public void handleReply(String msg) {
		Println(msg);
		Print(CONSOLE_MESSAGE);
	}
	
	private void handleInputUser(String inputOption) throws Exception {
		String[] tokens = inputOption.trim().split("\\s");
		if(tokens.length > 0) {
			String command = tokens[0].toLowerCase();
			logger.info("Input command " + command);
			
			if(command.compareTo(Commands.CONNECT) == 0){
				this.connect(tokens);
			}
			else if(command.compareTo(Commands.DISCONNECT) == 0){
				this.disconnect(tokens);				
			}
			else if(command.compareTo(Commands.HELP) == 0){
				Println("This is your EchoClient application.");
				PrintHelp();
				Print(CONSOLE_MESSAGE);
			}
			else if(command.compareTo(Commands.LOG_LEVEL) == 0){
				setLogLevel(tokens);
				Print(CONSOLE_MESSAGE);
			}
			else if(command.compareTo(Commands.QUIT) == 0){
				this.isRunning = false;				
			//}
			//else if(command.compareTo(Commands.SEND) == 0){
			//	send(tokens);
			} else if(command.compareTo(Commands.PUT) == 0){				
				put(tokens);
			}
			else if(command.compareTo(Commands.GET) == 0){				
				get(tokens);
			}
			else
			{
				Println("Unknown Command");
				PrintHelp();
				Print(CONSOLE_MESSAGE);
			}
		}
	}

	private void get(String[] tokens) throws Exception {
		if (storage != null) {
			if (tokens.length < 2 ){
				Println("Incorrect amount of parameters");
			}
			else{						
				Println("Getting Value... ");
				KVMessage reply = storage.get(tokens[1]);
				Println(reply.toString());
				Print(CONSOLE_MESSAGE);
			}
		} else {
			Println("Storage not available");
		}
	}

	private void put(String[] tokens) throws Exception {
		if (storage != null) {
			if (tokens.length < 2 ){
				Println("Incorrect amount of parameters");
			}
			else{
				String value = "";
				String key = tokens[1];
				if (tokens.length > 2)
					value = tokens[2];
				Println("Putting Value... ");
				KVMessage reply = storage.put(key, value);
				Println(reply.toString());
				Print(CONSOLE_MESSAGE);
			}
		} else {
			Println("Storage not available");
		}			
	}

	private void setLogLevel(String[] tokens) {
		if ( tokens.length == 1 ){
			Println(String.format("Log Level %s", logger.getLevel()));
		} else if ( tokens.length == 2 ){
			LoggingManager.SetLoggerLevel(tokens[1]);							
			Println(String.format("New Log Level is %s", logger.getLevel()));
		} else {
			Println("Incorrect amount of parameters");
		}
	}

	private void disconnect(String[] tokens) throws Exception {
		if (tokens.length > 1 ){
			Println("Incorrect amount of parameters");
		}
		else{
			if (storage != null)
				storage.disconnect();
			storage = null;
			logger.debug("Disconnect executed OK");
		}
	}

	private void connect(String[] tokens) throws Exception {
		if (tokens.length != 3 ){
			Println("Incorrect amount of parameters");
			Print(CONSOLE_MESSAGE);
		}
		else{
			storage = new KVStore(tokens[1], Integer.parseInt(tokens[2]));
			storage.AddListener(this);
			storage.connect();
			logger.debug("Connect executed OK");
		}
	}

	public void Print(String message){
		System.out.print(message);
	}
	
	public void Println(String message){
		System.out.println(message);
	}

	public void PrintHelp()
	{		
		Println("The following are the commands available:");
		Println("   " + Commands.CONNECT + " <ip address>" + " <port number>" );
		Println("   " + Commands.DISCONNECT);
		Println("   " + Commands.HELP);
		Println("   " + Commands.LOG_LEVEL + "[<New Log Level to set>]");
		Println("   " + Commands.SEND + " <message to be sent. Max size of msg 128kb");
		Println("   " + Commands.QUIT);
	}
}
